package com.dj.mingkong.portal.biz.service.music.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dj.mingkong.common.constant.RedisConstants;
import com.dj.mingkong.common.infrastructure.eo.database.music.MusicianEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.MusicianVisitEo;
import com.dj.mingkong.common.microservice.UserContext;
import com.dj.mingkong.common.util.AssertUtils;
import com.dj.mingkong.portal.biz.controller.music.vo.request.MusicianVisitAddVo;
import com.dj.mingkong.portal.biz.controller.music.vo.request.MusicianVisitCancelVo;
import com.dj.mingkong.portal.biz.controller.music.vo.request.MusicianVisitQueryVo;
import com.dj.mingkong.portal.biz.controller.music.vo.request.MusicianVisitUserQueryVo;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicianVisitUserVo;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicianVisitVo;
import com.dj.mingkong.portal.biz.core.base.context.UserContextHolder;
import com.dj.mingkong.portal.biz.repository.database.music.MusicianRepository;
import com.dj.mingkong.portal.biz.repository.database.music.MusicianVisitRepository;
import com.dj.mingkong.portal.biz.service.music.MusicianVisitService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 音乐人访客服务实现
 *
 * @author zenglonghui
 * @date: 2022/8/23 11:01
 */
@Slf4j
@Service("musicianVisitService")
public class MusicianVisitServiceImpl implements MusicianVisitService {
    @Resource
    private MusicianVisitRepository musicianVisitRepository;
    @Resource
    private MusicianRepository musicianRepository;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private UserContextHolder userContextHolder;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(MusicianVisitAddVo addVo) {
        final MusicianEo musicianEo = musicianRepository.getById(addVo.getMusicianId());
        AssertUtils.isNotNull(musicianEo, "musicianId invalid");
        //添加人气
        addPopularity(addVo);
        userContextHolder.getCurrentUserOpt().ifPresent(userInfoContext -> {
            final String key = RedisConstants.VISIT_MUSICIAN
                    .concat(userInfoContext.getId().toString())
                    .concat(RedisConstants.KEY_SPLIT)
                    .concat(addVo.getMusicianId().toString());
            if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
                saveMusicianVisit(key, userInfoContext.getId(), musicianEo);
            }
        });
    }

    @Override
    public Page<MusicianVisitVo> pageByVo(MusicianVisitQueryVo queryVo) {
        return musicianVisitRepository.getMapper()
                .listPage(Page.of(queryVo.getPage(), queryVo.getPageSize()), queryVo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancel(MusicianVisitCancelVo cancelVo) {
        final MusicianEo musicianEo = musicianRepository.getById(cancelVo.getMusicianId());
        AssertUtils.isNotNull(musicianEo, "MusicianId invalid");
        final Optional<MusicianVisitEo> optional = musicianVisitRepository
                .oneOptByUserIdAndMusicianId(UserContext.getUserId(), cancelVo.getMusicianId());
        optional.ifPresent(musicianVisitEo -> {
            musicianVisitRepository.deleteById(musicianVisitEo.getId());
            musicianRepository.updateById(musicianEo.setVisitCount(musicianEo.getVisitCount() - 1));
        });
    }

    @Override
    public Page<MusicianVisitUserVo> pageUserByVo(MusicianVisitUserQueryVo queryVo) {
        return musicianVisitRepository.getMapper()
                .listUserPage(Page.of(queryVo.getPage(), queryVo.getPageSize()), queryVo);
    }

    private void saveMusicianVisit(String key, Long userId, MusicianEo musicianEo) {
        final Optional<MusicianVisitEo> eoOptional = musicianVisitRepository
                .oneOptByUserIdAndMusicianId(userId, musicianEo.getId());
        eoOptional.ifPresentOrElse(musicianVisitEo -> {
            log.info("更新访客信息 userId:{}, musicianId:{}", userId, musicianEo.getId());
            musicianVisitRepository.updateById(musicianVisitEo);
            redisTemplate.opsForValue().set(key, RedisConstants.VISIT_MUSICIAN, 1, TimeUnit.MINUTES);
        }, () -> {
            log.info("保存访客信息 userId:{}, musicianId:{}", userId, musicianEo.getId());
            final MusicianVisitEo visitEo = MusicianVisitEo.builder().userId(userId)
                    .musicianId(musicianEo.getId()).build();
            musicianVisitRepository.save(visitEo);
            redisTemplate.opsForValue().set(key, RedisConstants.VISIT_MUSICIAN, 1, TimeUnit.MINUTES);
            musicianRepository.updateById(musicianEo.setVisitCount(musicianEo.getVisitCount() + 1));
        });
    }

    private void addPopularity(MusicianVisitAddVo addVo) {
        final var rand = new SecureRandom();
        final long num = rand.nextLong(10) + 1;
        final Long increment = redisTemplate.boundHashOps(RedisConstants.POPULARITY_MUSICIAN)
                .increment(addVo.getMusicianId(), num);
        log.info("添加音乐人人气 id :{}, 点击数： {}", addVo.getMusicianId(), increment);
    }
}
